/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package algorithms;

import java.util.ArrayList;
import util.Constants;
import mopso.MetricsReport;
import mopso.PSO;
import mopso.Particle;
import mopso.PerformanceMetrics;
import mopso.PlotManager;
import mopso.Problem;
import mopso.Repository;
import util.Turbulence;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.axis.NumberAxis;

public class MOPSO_CDR {

    private ArrayList<Particle> swarm;
    private ArrayList<Particle> externalArchive;
    private Problem problem;
    private int nParticles;
    private int nMaxParticlesExternalArchive;
    private int nIterations;    
    private int currentIteration;
    private int currentNCallObjectiveFunction;
    private int beforeNCallObjectiveFunction;
    private double inertia;
    private int maxPbests;
    private int maxExternalArchive;
    private double standardDeviationMutationGaussian;
    private Particle auxParticleCurrentPbest;
    private Particle auxParticle2;
    private Particle auxParticle3;
    private Repository repositoryOperators;
    private Turbulence turbulenceOperators;
    private PSO psoOperators;
    private PlotManager plotManager;
    private PerformanceMetrics perfomanceMetrics;
    private double hypervolume;
    private double spacing;
    private double maximumSpread;
    private double timeDuration;
    private long startTimeInMs;
    private long elapsedTimeInMs;

    public MOPSO_CDR(Problem problem) {

        this.repositoryOperators = new Repository();
        this.turbulenceOperators = new Turbulence();
        this.psoOperators = new PSO();
        this.plotManager = new PlotManager();
        this.plotManager.setupPlotExternalArchive();
        this.plotManager.setupPlotSwarm();

        this.plotManager.setDebug(false);
        this.perfomanceMetrics = new PerformanceMetrics();

        this.swarm = new ArrayList<Particle>();
        this.externalArchive = new ArrayList<Particle>();
        
        this.nIterations = 10000;

        this.problem = problem;
        this.nParticles = Constants.SWARMSIZE;
        this.nMaxParticlesExternalArchive = Constants.EXTERNALARCHIVESIZE;
        this.inertia = 0.4;
        this.standardDeviationMutationGaussian = Constants.INITIALSDMUTATIONGAUSSIAN;

        this.auxParticleCurrentPbest = new Particle(problem);
        this.auxParticle2 = new Particle(problem);
        this.auxParticle3 = new Particle(problem);

        System.out.println("\nTécnica: MOPSO_CDR");
        System.out.println("Problema: " + problem.getName());

        startTimeInMs = System.currentTimeMillis();

        // Inicializando swarm
        System.out.println("Inicializando o enxame...");

        this.initializeSwarm();
    }

    public void run() {

        System.out.println("Calculando fitness das partículas...");

        for (int i = 0; i < nParticles; i++) {
            swarm.get(i).calculateFitness();
            swarm.set(i, repositoryOperators.updatePbest(swarm.get(i), problem));
        }

        System.out.println("Inicializando líderes...");
        externalArchive = repositoryOperators.initializeExternalArchive(externalArchive, swarm, problem);

        System.out.println("Qualificando líderes por crowding distance...");
        setExternalArchive(repositoryOperators.qualityExternalArchive(getExternalArchive(), problem));

        this.currentNCallObjectiveFunction = 0;
        this.beforeNCallObjectiveFunction = 0;
        this.currentIteration = 0;

        while (currentNCallObjectiveFunction < Constants.NCALLOBJECTIVEFUNCTION) {
            swarm = turbulenceOperators.turbulence_MOPSO(swarm, problem, currentNCallObjectiveFunction, Constants.NCALLOBJECTIVEFUNCTION, Constants.MUTATIONPERCENTUALMOPSO);

            for (int j = 0; j < nParticles; j++) {
                if (currentNCallObjectiveFunction < Constants.NCALLOBJECTIVEFUNCTION) {
                    Particle particle = swarm.get(j);

                    particle = repositoryOperators.selectLeader(particle, externalArchive);
                    particle = repositoryOperators.selectLeaderPbest(particle);

                    particle = psoOperators.updatePosition(particle, problem, inertia, Constants.COGNITIONLEARNINGRATE, Constants.SOCIALLEARNINGRATE);

                    particle.calculateFitness();
                    currentNCallObjectiveFunction++;

                    if ((currentNCallObjectiveFunction % 10000) == 9999) {
                        plotManager.plotExternalArchive(getExternalArchive(), currentNCallObjectiveFunction + 1);
                        plotManager.plotSwarm(swarm, currentNCallObjectiveFunction + 1);
                    }

                    particle = repositoryOperators.updatePbestByNearCD(particle, externalArchive, problem);

                    if (particle.getPbests().size() > maxPbests) {
                        maxPbests = particle.getPbests().size();
                    }
                }
            }

            externalArchive = repositoryOperators.updateExternalArchive(getExternalArchive(), swarm, problem);
            externalArchive = repositoryOperators.qualityExternalArchive(externalArchive, problem);
            externalArchive = repositoryOperators.trunkExternalArchive(externalArchive, nMaxParticlesExternalArchive);

            if (externalArchive.size() > maxExternalArchive) {
                maxExternalArchive = externalArchive.size();
            }

            inertia = psoOperators.updateLinearInertia(0.4, 0.0, currentNCallObjectiveFunction, Constants.NCALLOBJECTIVEFUNCTION);

            currentIteration++;
        }

        elapsedTimeInMs = System.currentTimeMillis() - startTimeInMs;

        plotManager.plotExternalArchive(externalArchive, currentNCallObjectiveFunction);
        plotManager.plotSwarm(swarm, currentNCallObjectiveFunction);

        plotManager.getJFreeChartExternalArchive().setTitle("MOPSO-CDR");

        System.out.println("Tamanho maximo atingido pela lista de Pbests: " + maxPbests);
        System.out.println("Tamanho maximo atingido pelo arquivo externo: " + maxExternalArchive);
        System.out.println("Tamanho final do arquivo externo: " + externalArchive.size());

        this.calculateMetrics();
    }

    void calculateMetrics() {
//        generationalDistance = perfomanceMetrics.calculateGenerationalDistance(swarm,getExternalArchive(), problem);
        hypervolume = perfomanceMetrics.calculateHypervolume2d(externalArchive, problem);
//        spread = perfomanceMetrics.calculateSpread(swarm,getExternalArchive(), problem);
        spacing = perfomanceMetrics.calculateSpacing(externalArchive, problem);
        maximumSpread = perfomanceMetrics.calculateMaximumSpread(externalArchive, problem);
        timeDuration = (double) (elapsedTimeInMs / 1000.0);
//        gmean = perfomanceMetrics.calculateGMean(getExternalArchive());

//        System.out.println("Generational Distance: " + generationalDistance);
        System.out.println("Hypervolume: " + hypervolume);
//        System.out.println("Spread: " + spread);
        System.out.println("Spacing: " + spacing);
        System.out.println("Maximum Spread: " + maximumSpread);
        System.out.println("Time Duration (sec.): " + timeDuration);
//        System.out.println("g() mean: " + gmean);
    }

    private void initializeSwarm() {
        for (int i = 0; i < nParticles; i++) {
            swarm.add(new Particle(problem, i));
        }
    }

    public ChartPanel getChartPanelExternalArchive() {
        return plotManager.getChartPanelExternalArchive();
    }

    public void setChartPanelExternalArchive(ChartPanel chartPanelExternalArchive) {
        plotManager.setChartPanelExternalArchive(chartPanelExternalArchive);
    }

    public ChartPanel getChartPanelSwarm() {
        return plotManager.getChartPanelSwarm();
    }

    public void setChartPanelSwarm(ChartPanel chartPanelSwarm) {
        plotManager.setChartPanelSwarm(chartPanelSwarm);
    }
    
    public NumberAxis getNumberAxisExternalArchive() {
        return plotManager.getNumberAxisExternalArchive();
    }    

    public ArrayList<Particle> getExternalArchive() {
        return externalArchive;
    }

    public void setExternalArchive(ArrayList<Particle> externalArchive) {
        this.externalArchive = externalArchive;
    }

    public MetricsReport getMetrics() {
        return new MetricsReport(hypervolume, spacing, maximumSpread, timeDuration);
    }
}
